﻿///  Author     :       Bonnot Guillaume
///  Class      :       SynchronousPulsar
///  Purpose    :       Game Developement, use this as the main loop of your computing thread
///  
///  ********* Description *********
///  The SynchronousPulsar is used call an action N times per seconds.
///  SynchronousPulsar.pulse() will loop and perform "action" every 1/N seconds.
///  The loop breaks when action returns false.
///  SynchronousPulsar permits a specific use of spared CPU time.
///  If "action" doesnt consume all the allocated time, "idle" will be called.
///  If "action" does consume more than the allocated time, "busy" will be called.
///  Note that "busy" and "idle" execution time will be added to "action".
///  *******************************

/*  Exemple given :

    // a pulsar wich will do 25 actions per seconds
    SynchronousPulsar pulsar = new SynchronousPulsar(25);

    // the computing 'action" you have to do at every tick
    pulsar.action += new SynchronousPulsar.ActionHandler(my_action_function);

    // the function that will be called if "action" was too long
    pulsar.busy += new SynchronousPulsar.TimeHandler(my_busy_function);

    // the function that will be called if there is some time remaining after "action"
    pulsar.idle += new SynchronousPulsar.TimeHandler(my_idle_function);

    // the loop
    pulsar.pulse();

*/

/* Typical use : Game Server
    
    // the server update the game instance
    public bool my_action_function() {
        // fetch messages
        // update
        // send messages
    }

    public void my_busy_function(int ms) {
        // if it happens frequently,
        // log that server is overloaded
    }

    public void my_idle_function(int remaining) {
        // yield CPU time for other threads
        System.Threading.Thread.Sleep(remaining);
    }
*/

/* Typical use : Game Client
    
    // the update the game
    public bool my_action_function() {
        // fetch messages
        // update
        // send messages
    }

    // use remaining time for max FPS
    public void my_idle_function(int remaining) {
        // Draw GUI
    }
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BomberManServer.Utils
{
    class SynchronousPulsar
    {
        // the function that will be called every 1/N seconds
        public delegate bool ActionHandler();
        public ActionHandler action;

        // Handler with ms as milliseconds
        // Must be positive
        public delegate void TimeHandler(int ms);
        public TimeHandler busy;
        public TimeHandler idle;

        private uint actions_per_second;
        // planned execution time per tick
        private int ms_per_action;

        public SynchronousPulsar(uint actions_per_second)
        {
            this.actions_per_second = actions_per_second;
            ms_per_action = (int)(1000 / actions_per_second);
        }

        public void pulse()
        {
            int next_action, remaining, now;

            // register time
            next_action = Environment.TickCount;

            while (true)
            {
                // store time for next action
                next_action += ms_per_action;

                // do stuff and exit loop if action returns false
                if(!action())                    
                    break;

                // get new time
                now = Environment.TickCount;
                remaining = next_action - now;

                // look if we have time remaining
                if (remaining > 0)
                {
                    if (idle != null)
                        idle(remaining);
                }
                else
                {
                    if (busy != null)
                        // remaining is negative, so we take its absolute value
                        busy(Math.Abs(remaining));
                }
            }
        }
    }
}